# coding:utf-8
import copy
import math
import time

from couchdb import ResourceNotFound

from block import Block
from block_header import BlockHeader
from cnrp import CNRP_WEIGHT_DICT, LocalCNRP, CNRP_WINDOW_LENGTH, CNRP_R_init, CNRP_threshold_rp_num, \
    CNRP_S_virtual_node, CNRP_b_punish, CNRP_E_init
from db import DB
from node_file import NodeFile
from transaction import *
# from errors import NotEnoughAmountError
# from utils_lib.utils import address_to_pubkey_hash
from conf import db_url


# Block chain class 块头类
# by liang fei 2021.06.28


# class: BlockChain
# the block chain = block + others
# Attributes:
#     timestamp (str): 时间戳 创建块头对象时的时间
#     prev_block_hash (str): 上一块的hash
#     block_hash (str): 当前块的hash
#     hash_merkle_root (str): Hash of the merkle_root
#     reputation_threshold (int): 信誉值 阈值
#     reputation (int): 当前块的信誉值
#     height (int): 块高度
class BlockChain(object):

    def __init__(self, db_url=db_url):
        self.db = DB(db_url)

    def new_genesis_block(self, transaction):
        if 'l' not in self.db:
            genesis_block = Block.new_genesis_block(transaction)
            genesis_block.set_header_hash()

            # set genesis block cnrp
            # genesis_block.block_header.set_cnrp(
            #     {transaction.tx_content.public_key_hash: CNRP_WEIGHT_DICT['basic']+CNRP_WEIGHT_DICT['genesis_block']})

            self.db.create(genesis_block.block_header.block_hash, genesis_block.serialize())
            self.set_last_hash(genesis_block.block_header.block_hash)
        else:
            return

    def get_last_block(self):
        last_block_hash_doc = self.db.get('l')
        if not last_block_hash_doc:
            return None
        last_block_hash = last_block_hash_doc.get('hash', '')
        block_data = self.db.get(last_block_hash)
        # block_data = self.db[last_block_hash]
        block = Block.deserialize(block_data)
        return block

    def set_last_hash(self, _hash):
        last_hash = {"hash": str(_hash)}
        if 'l' not in self.db:
            self.db['l'] = last_hash
        else:
            last_block_hash_doc = self.db.get('l')
            last_block_hash_doc.update(hash=_hash)
            self.db.update([last_block_hash_doc])

    # 数据库查询
    def get_block_by_height(self, height):
        """
        Get a block by height
        """
        # query = {"selector": {"block_header": {"height": height}}}
        query = {"height": height}
        docs = self.db.find(query)
        block = None
        for doc in docs:
            block = Block.deserialize(doc)
        return block

    def roll_back(self):
        last_block = self.get_last_block()
        last_height = last_block.block_header.height
        block_doc = self.db.get(last_block.block_header.hash)
        try:
            self.db.delete(block_doc)
        except ResourceNotFound as e:
            print(e)
        block = self.get_block_by_height(last_height-1)
        self.set_last_hash(block.block_header.hash)

    def get_block_by_hash(self, _hash):
        """
        Get a block by hash
        """
        block_data = self.db.get(_hash)
        block = Block(None, None)
        block.deserialize(block_data)
        return block

    def add_block(self, transaction):
        """
        add a block to block_chain
        """
        last_block = self.get_last_block()

        prev_hash = last_block.get_header_hash()

        height = last_block.block_header.height + 1

        # merkle 值为''
        block_header = BlockHeader('', height, prev_hash)

        block = Block(block_header, transaction)

        # 更新cnrp
        block = self.update_block_rp(block)
        self.update_local_cnrp(block)

        block.mine(self)
        block.set_header_hash()
        self.db.create(block.block_header.block_hash, block.serialize())

        last_hash = block.block_header.block_hash
        self.set_last_hash(last_hash)
    
    def add_block_from_peers(self, block):
        last_block = self.get_last_block()

        if last_block:
            prev_hash = last_block.get_header_hash()
            last_height = last_block.block_header.height

            if block.block_header.height < last_height:
                raise ValueError('block height is error')

            if not block.validate(self):
                raise ValueError('block is not valid')

            if block.block_header.height == last_height and block != last_block:
                self.roll_back()

            if block.block_header.height == last_height+1 and block.block_header.prev_block_hash == last_block.block_header.block_hash:
                self.db.create(block.block_header.block_hash, block.serialize())
                last_hash = block.block_header.block_hash
                self.set_last_hash(last_hash)
                self.update_local_cnrp(block)

        else:
            self.db.create(block.block_header.block_hash, block.serialize())
            last_hash = block.block_header.block_hash
            self.set_last_hash(last_hash)
            self.update_local_cnrp(block)
    
    def __getitem__(self, index):
        last_block = self.get_last_block()
        height = -1
        if last_block:
            height = last_block.block_header.height
        if index <= height:
            return self.get_block_by_height(index)
        else:
            raise IndexError('Index is out of range')

    def new_transaction(self, sub_tx_obj):
        node_file = NodeFile()
        private_key = node_file['local_node'].node_private_key
        tx = Transaction(sub_tx_obj)
        tx.set_id()
        tx.sign(private_key)
        return tx

    # 可能不完善 需要修改
    def coin_base_tx(self, to_addr):
        data = str(time.time())
        tx = Transaction.coinbase_tx(to_addr, data)
        return tx

    # ==================================== 查询相关 =======================================================

    # 当前 block 与 tx 对应 即: 单个block对应单个tx 以下查询均基于以上前提

    # 找到唯一block
    def find_block_by_id(self, tx_id):

        # method 1: 遍历查询
        # last_block = self.get_last_block()
        # last_height = last_block.block_header.height
        # for height in range(last_height, -1, -1):
        #     block = self.get_block_by_height(height)
        #     if block.transactions.tx_id == tx_id:
        #         return block
        # return None

        # method 2: Query 查询
        # query = {"selector": {"transaction": {"tx_id": tx_id}}}
        query = {"tx_id": tx_id}
        docs = self.db.find(query)
        block = None
        for doc in docs:
            block = Block.deserialize(doc)
        return block

    # 返回一个list
    def find_block_by_type(self, tx_type):

        # method 1: 遍历查询
        # last_block = self.get_last_block()
        # last_height = last_block.block_header.height
        # res = []
        # for height in range(last_height, -1, -1):
        #     block = self.get_block_by_height(height)
        #     if block.transactions.tx_type == tx_type:
        #         res.append(block)
        # return res

        # method 2: Query 查询
        # query = {"selector": {"transaction": {"tx_type": tx_type}}}
        query = {"tx_type": tx_type}
        docs = self.db.find(query)
        block_list = []
        for doc in docs:
            block_list.append(Block.deserialize(doc))
        return block_list

    # 返回一个list: 找到data_id 的所有block
    def find_block_by_data_id(self, data_id):

        # method 1: 遍历查询
        # last_block = self.get_last_block()
        # last_height = last_block.block_header.height
        # res = []
        # for height in range(last_height, -1, -1):
        #     block = self.get_block_by_height(height)
        #     if block.transactions.data_id == data_id:
        #         res.append(block)
        # return res

        # method 2: Query 查询
        # query = {"selector": {"transaction": {"data_id": data_id}}}
        query = {"data_id": data_id}
        docs = self.db.find(query)
        block_list = []
        for doc in docs:
            block_list.append(Block.deserialize(doc))
        return block_list

    # 返回一个list
    def find_xtype_block_by_data_id(self, data_id, tx_type):

        # method 1: 遍历查询
        # block_list = self.find_block_by_data_id(data_id)
        # res_list = []
        # for block in block_list:
        #     if block.transactions.tx_type == tx_type:
        #         res_list.append(block)
        # return res_list

        # method 2: Query 查询
        # query = {"selector": {"transaction": {"data_id": data_id, "tx_type": tx_type}}}
        query = {"data_id": data_id, "tx_type": tx_type}
        docs = self.db.find(query)
        block_list = []
        for doc in docs:
            block_list.append(Block.deserialize(doc))
        return block_list

    # 返回一个list 包含4个元素 依次为 upload-request-share-evaluate
    def find_block_by_business_id(self, business_id):

        # method 1: 遍历查询
        # last_block = self.get_last_block()
        # last_height = last_block.block_header.height
        # res_list = []
        # for height in range(last_height, -1, -1):
        #     block = self.get_block_by_height(height)
        #     if block.transactions.business_id == business_id:
        #         res_list.append(block)
        #     if block.transactions.tx_type == 'request':
        #         res_list.append(self.find_block_by_id(block.transactions.tx_content.previous_tx_id))
        #         break
        # res_list.reverse()
        # return res_list

        # method 2: Query 查询
        # query = {"selector": {"transaction": {"business_id": business_id}}}
        query = {"business_id": business_id}
        docs = self.db.find(query)
        block_list = []
        for doc in docs:
            block_list.append(Block.deserialize(doc))
        if not block_list:
            return []
        else:
            # print(block_list)
            # print(type(block_list))
            # print(len(block_list))
            for block in block_list:
                # print(block)
                # print(type(block))
                if block.transactions.tx_type == 'request':
                    block_list.append(self.find_block_by_id(block.transactions.previous_tx_id))
        # 整理顺序
        block_list_copy = copy.deepcopy(block_list)
        block_list = [None, None, None, None]
        type_dict = {'upload': 0, 'request': 1, 'share': 2, 'evaluate': 3}
        for block in block_list_copy:
            block_list[type_dict[block.transactions.tx_type]] = block
        # 去除None
        for i in range(len(block_list)):
            if not block_list[i]:
                block_list = block_list[:i]
                break
        return block_list

    # 返回一个list: 根据block来源地址查询block <public_key_hash>
    def find_block_by_node_address(self, address):

        # method 1: 遍历查询
        # last_block = self.get_last_block()
        # last_height = last_block.block_header.height
        # res = []
        # for height in range(last_height, -1, -1):
        #     block = self.get_block_by_height(height)
        #     if block.transactions.tx_content.public_key_hash == address:
        #         res.append(block)
        # return res

        # method 2: Query 查询
        # query = {"selector": {"transaction": {"tx_content": {"public_key_hash": address}}}}
        query = {"public_key_hash": address}
        docs = self.db.find(query)
        block_list = []
        for doc in docs:
            block_list.append(Block.deserialize(doc))
        return block_list

    # 返回一个list: 根据block来源地址查询block <source_node_address>
    def find_block_by_node_key(self, source_node_address):

        # method 1: 遍历查询
        # last_block = self.get_last_block()
        # last_height = last_block.block_header.height
        # res = []
        # for height in range(last_height, -1, -1):
        #     block = self.get_block_by_height(height)
        #     if block.transactions.tx_content.source_node_address == source_node_address:
        #         res.append(block)
        # return res

        # method 2: Query 查询
        # query = {"selector": {"transaction": {"tx_content": {"source_node_address": source_node_address}}}}
        query = {"source_node_address": source_node_address}
        docs = self.db.find(query)
        block_list = []
        for doc in docs:
            block_list.append(Block.deserialize(doc))
        return block_list

    # 找到别人对指定node的所有类型块
    # address: tx.tx_content.public_key_hash
    # subject <str>: 'act' / 'pas' 动作执行者/动作接受者（是他人对address的 还是address对他人的）
    def find_xtype_block_relate_address(self, address, xtype='all', subject='all'):

        if xtype not in ['all', 'upload', 'request', 'share', 'evaluate']:
            print('Function- find_xtype_block_relate_address(): wrong type')
            return
        if subject not in ['all', 'act', 'pas']:
            print('Function- find_xtype_block_relate_address(): wrong subject')
            return

        def _judge_act_xtype(_block):
            if _block.transactions.tx_content.public_key_hash == address and _block.transactions.tx_type == xtype:
                    return True
            return False

        def _judge_act_all(_block):
            if _block.transactions.tx_content.public_key_hash == address:
                return True
            return False

        def _judge_pas_upload(_block):
            return False

        def _judge_pas_request_evaluate(_block):
            if address in block.transactions.business_id and address in block.transactions.data_id:
                if _block.transactions.tx_type == xtype:
                    return True
            return False

        def _judge_pas_share(_block):
            if address in block.transactions.business_id and address not in block.transactions.data_id:
                if _block.transactions.tx_type == xtype:
                    return True
            return False

        def _judge_pas_all(_block):
            if address in block.transactions.business_id or address in block.transactions.data_id:
                if _block.transactions.tx_content.public_key_hash != address:
                    return True
            return False

        def _judge_all_xtype(_block):
            if address in block.transactions.business_id or address in block.transactions.data_id:
                if _block.transactions.tx_type == xtype:
                    return True
            return False

        def _judge_all_all(_block):
            if address in block.transactions.business_id or address in block.transactions.data_id:
                return True
            return False

        _judge = {
            'act': {
                'upload': _judge_act_xtype,
                'request': _judge_act_xtype,
                'share': _judge_act_xtype,
                'evaluate': _judge_act_xtype,
                'all': _judge_act_all,
            },
            'pas': {
                'upload': _judge_pas_upload,
                'request': _judge_pas_request_evaluate,
                'share': _judge_pas_share,
                'evaluate': _judge_pas_request_evaluate,
                'all': _judge_pas_all,
            },
            'all': {
                'upload': _judge_all_xtype,
                'request': _judge_all_xtype,
                'share': _judge_all_xtype,
                'evaluate': _judge_all_xtype,
                'all': _judge_all_all,
            }
        }

        last_block = self.get_last_block()
        last_height = last_block.block_header.height
        res = []
        for height in range(last_height, -1, -1):
            block = self.get_block_by_height(height)
            if _judge[subject][xtype](block):
                res.append(block)
        return res

    # ---------------------------------

    # 找到唯一tx
    def find_tx_by_id(self, tx_id):
        block = self.find_block_by_id(tx_id)
        if block:
            return block.transactions
        return None

    # 返回一个list
    def find_tx_by_type(self, tx_type):
        block_list = self.find_block_by_type(tx_type)
        res_list = []
        for block in block_list:
            res_list.append(block.transactions)
        return res_list

    # 返回一个list: 找到data_id 的所有tx
    def find_tx_by_data_id(self, data_id):
        block_list = self.find_block_by_data_id(data_id)
        res_list = []
        for block in block_list:
            res_list.append(block.transactions)
        return res_list

    # 返回一个list
    def find_xtype_tx_by_data_id(self, data_id, tx_type):
        block_list = self.find_xtype_block_by_data_id(data_id, tx_type)
        res_list = []
        for block in block_list:
            res_list.append(block.transactions)
        return res_list

    # 返回一个list
    def find_tx_by_business_id(self, business_id):
        block_list = self.find_block_by_business_id(business_id)
        res_list = []
        for block in block_list:
            res_list.append(block.transactions)
        return res_list

    # 返回一个list: 根据block来源地址查询block <public_key_hash>
    def find_tx_by_node_address(self, address):
        block_list = self.find_block_by_node_address(address)
        res_list = []
        for block in block_list:
            res_list.append(block.transactions)
        return res_list

    # 返回一个list: 根据block来源地址查询block <source_node_address>
    def find_tx_by_node_key(self, source_node_address):
        block_list = self.find_block_by_node_key(source_node_address)
        res_list = []
        for block in block_list:
            res_list.append(block.transactions)
        return res_list

    def find_xtype_tx_relate_address(self, address, xtype='all', subject='all'):
        block_list = self.find_xtype_block_relate_address(address, xtype, subject)
        res_list = []
        for block in block_list:
            res_list.append(block.transactions)
        return res_list

    # ================================ over ====================================================

    # 根据 business_id 验证待发布的 block 是否合法
    # 通俗讲：验证给定address是否可以上传给定type的指向prev_tx_id的块
    # node_address: 待上传的block使用的地址 一般为本地地址 public_key_hash
    # prev_tx_id: 待上传的block所指向的前向block
    # tx_type:待上传block的类型
    # 注意：调用之前首先应确认 prev_tx_id 有效
    def verify_business_process(self, node_address, prev_tx_id, tx_type):

        prev_tx = self.find_tx_by_id(prev_tx_id)
        if not prev_tx:
            return

        tx_list = self.find_tx_by_business_id(prev_tx.business_id)
        tx_list_len = len(tx_list)

        if tx_list_len == 0:
            return True
        if tx_list_len == 1:
            if tx_type == 'request':
                return True
            else:
                return False
        elif tx_list_len == 2:
            if tx_type == 'share' and node_address == tx_list[0].tx_content.public_key_hash:
                return True
            else:
                return False
        elif tx_list_len == 3:
            if tx_type == 'evaluate' and node_address == tx_list[1].tx_content.public_key_hash:
                return True
            else:
                return False
        elif tx_list_len == 4:
            return False
        else:
            return False

    # 此处修改不完善 可能会有bug
    def sign_transaction(self, tx, private_key):
        tx.sign(private_key)

    def verify_transaction(self, tx):
        return tx.verify(tx)

    # ==================== about CNRP -2021.09.27 ===============================

    # 创建新block时计算新block的信誉表 global_rp
    # @note: 用于上传新块之前调用 新块new完毕之后使用该函数计算填充block_header中的reputation属性<global_rp>
    # @param: block <block object>: 新的待发布的block
    # return: block <block object>: 填充<global_rp>后的block
    # 大更新: (2021.10.26)
    # 将 CNRP 分为 number 和 value 两部分(2021.10.26)
    def update_block_rp(self, block):

        # 注意：在加入新节点的时候调用，所以必要时要考虑到流程验证

        # list 与 str 相互转换
        def _switch(list_or_str):
            if isinstance(list_or_str, str):
                return [float(_) for _ in list_or_str.split(',')]
            else:
                return str(list_or_str[0]) + ',' + str(list_or_str[1])

        # 计算过程中 规范小数保留位数
        def _round(_x):
            return round(_x, 6)

        # 计算 S
        def _compute_S(_pas_neg):
            _S_top = _pas_neg[0] - CNRP_b_punish * _pas_neg[1]
            _S_btm = _pas_neg[0] + CNRP_b_punish * _pas_neg[1]
            return _S_top / _S_btm

        cur_height = block.block_header.height
        block_tx_type = block.transactions.tx_type

        prev_cnrp = copy.deepcopy(self.get_block_by_height(block.block_header.height - 1).block_header.cnrp)

        block_source_address = block.transactions.tx_content.public_key_hash

        # number dict
        cnrp_number = prev_cnrp.get('cnrp_number', {})

        # evaluate dict
        # [被评价的目标地址]: {[评价的目标地址]: [积极评价数目,消极评价数目 <str>]}
        cnrp_evaluate = prev_cnrp.get('cnrp_evaluate', {})

        # fraction dict
        # [被评价的目标地址]: [分子,分母 <str>]
        cnrp_fraction = prev_cnrp.get('cnrp_fraction', {})

        # value dict
        # [地址]: [最终rp值 <int>]
        cnrp_value = prev_cnrp.get('cnrp_value', {})

        # 时间窗口 注：时间窗口只影响 B 的计算
        if cur_height > CNRP_WINDOW_LENGTH:
            con_height = cur_height - CNRP_WINDOW_LENGTH
            con_cnrp_number = self.get_block_by_height(con_height).block_header.cnrp.get('cnrp_number')

            if con_height > 1:
                con_con_cnrp_number = self.get_block_by_height(con_height - 1).block_header.cnrp.get('cnrp_number')
                diff_number = {}
                for k, v in con_con_cnrp_number.items():
                    if con_cnrp_number[k] != v:
                        diff_number[k] = con_cnrp_number[k] - v
            else:
                diff_number = con_cnrp_number

            for k, v in diff_number.items():
                cnrp_number[k] -= v

        if block_tx_type in ['upload', 'share', 'evaluate']:
            # 更新 number dict
            _prev_num = cnrp_number.get(block_source_address, 0)
            cnrp_number[block_source_address] = _prev_num + 1

            if block_tx_type == 'evaluate':
                target_address = self.find_block_by_id(block.transactions.previous_tx_id).transactions.tx_content.public_key_hash
                score = block.transactions.tx_content.data_evaluate

                R_source_address = cnrp_value.get(block_source_address, CNRP_R_init)

                if target_address not in cnrp_evaluate:
                    # 之前无该目标地址的记录 当前 evaluate 为收到的第一条打分
                    S = 1 if score >= 0.5 else 0

                    # 计算 E_b (当打分个数小于指定阈值时 补足虚拟节点的打分记录)
                    _speed_factor_e = 1 - math.exp(-1)
                    _speed_factor_e = _round(_speed_factor_e)

                    _speed_factor_top = (R_source_address ** 2) * _speed_factor_e
                    _speed_factor_top = _round(_speed_factor_top)

                    # 计算 E_b 分子
                    E_fraction_top = _speed_factor_top * S
                    E_fraction_top = _round(E_fraction_top)
                    E_fraction_top += (CNRP_R_init ** 2) * CNRP_S_virtual_node * _speed_factor_e * (CNRP_threshold_rp_num - 1)
                    E_fraction_top = _round(E_fraction_top)
                    # 计算 E_b 分母
                    E_fraction_btm = _speed_factor_top
                    E_fraction_btm = _round(E_fraction_btm)
                    E_fraction_btm += (CNRP_R_init ** 2) * _speed_factor_e * (CNRP_threshold_rp_num - 1)
                    E_fraction_btm = _round(E_fraction_btm)

                    _pos_neg = '1,0' if score >= 0.5 else '0,1'

                else:
                    # 有被评价地址的历史记录
                    cnrp_evaluate_target_address = cnrp_evaluate[target_address]
                    _fraction = _switch(cnrp_fraction[target_address])

                    if block_source_address not in cnrp_evaluate_target_address:
                        # 有被评价地址的历史记录 没有评价来源地址的打分记录(有其他地址对目标地址的打分记录)

                        S = 1 if score >= 0.5 else 0

                        _speed_factor_top = (R_source_address ** 2) * (1 - math.exp(-1))
                        _speed_factor_top = _round(_speed_factor_top)

                        E_fraction_top = _fraction[0] + _speed_factor_top * S
                        E_fraction_top = _round(E_fraction_top)

                        E_fraction_btm = _fraction[1] + _speed_factor_top
                        E_fraction_btm = _round(E_fraction_btm)

                        _pos_neg = '1,0' if score >= 0.5 else '0,1'

                    else:
                        # 有被评价地址的历史记录 有评价来源地址的打分记录
                        _source_address_evaluate = _switch(cnrp_evaluate_target_address[block_source_address])

                        S = _compute_S(_source_address_evaluate)
                        S = _round(S)

                        _speed_factor_e = (R_source_address ** 2) * (1 - math.exp(-sum(_source_address_evaluate)))
                        _speed_factor_e = _round(_speed_factor_e)

                        _E_fraction_top_diff = _speed_factor_e * S
                        _E_fraction_btm_diff = _speed_factor_e

                        if score >= 0.5:
                            _source_address_evaluate[0] += 1
                        else:
                            _source_address_evaluate[1] += 1

                        S = _compute_S(_source_address_evaluate)
                        S = _round(S)

                        _speed_factor_e = (R_source_address ** 2) * (1 - math.exp(-sum(_source_address_evaluate)))
                        _speed_factor_e = _round(_speed_factor_e)

                        E_fraction_top = _fraction[0] - _E_fraction_top_diff + _speed_factor_e * S
                        E_fraction_top = _round(E_fraction_top)

                        E_fraction_btm = _fraction[1] - _E_fraction_btm_diff + _speed_factor_e
                        E_fraction_btm = _round(E_fraction_btm)

                        _pos_neg = _switch(_source_address_evaluate)

                E = E_fraction_top / E_fraction_btm
                E = _round(E)

                # 计算第一阶段 B 值
                B = 1 - math.exp(-cnrp_number[target_address])
                B = _round(B)

                # 计算最终信誉值 R
                R = B * E
                R = _round(R)

                # 更新 target_address 的 dict
                cnrp_evaluate[target_address] = {block_source_address: _pos_neg}
                cnrp_fraction[target_address] = str(E_fraction_top) + ',' + str(E_fraction_btm)
                cnrp_value[target_address] = R

            # 非 evaluate 类型块时 使用上次的 E 值 否则使用初始值
            E = CNRP_E_init
            if block_source_address in cnrp_fraction:
                _fraction = _switch(cnrp_fraction.get(block_source_address))
                E = _fraction[0] / _fraction[1]
                E = _round(E)

            # 计算第一阶段 B 值
            B = 1 - math.exp(-cnrp_number[block_source_address])
            B = _round(B)

            # 计算最终信誉值 R
            R = B * E
            R = _round(R)

            # 更新 source_address 的 dict
            cnrp_value[block_source_address] = R

        # 将更新后的四个字典组装
        prev_cnrp['cnrp_number'] = cnrp_number
        prev_cnrp['cnrp_evaluate'] = cnrp_evaluate
        prev_cnrp['cnrp_fraction'] = cnrp_fraction
        prev_cnrp['cnrp_value'] = cnrp_value

        # 返回填充后的 block
        block.block_header.cnrp = prev_cnrp
        return block

    # 验证新block中的 global_rp 是否合法 新block指从peer中即将加入db的外来block
    def verify_block_rp(self, block):
        local_cnrp = self.update_block_rp(block).block_header.cnrp
        return block.block_header.cnrp == local_cnrp

    # 更新本地cnrp
    # 应用场景：新块产生即调用
    def update_local_cnrp(self, block):
        local_cnrp = LocalCNRP()

        # cur_last_height = self.get_last_block().block_header.height
        # cnrp_height = local_cnrp.get_height()

        node_file = NodeFile()
        if not node_file.has_local_node():
            return

        node = node_file['local_node']

        height = block.block_header.height

        if block.transactions.tx_type == 'upload':
            local_cnrp.refresh_height(height)
            pass
        elif block.transactions.tx_type == 'request':

            # “我”请求他人的数据
            if block.transactions.tx_content.public_key_hash == node.address:
                pass

            # 他人请求我的数据
            if node.address in block.transactions.data_id:
                local_cnrp.refresh(block.transactions.tx_content.public_key_hash, CNRP_WEIGHT_DICT['local_rp_request_o2m'])

            local_cnrp.refresh_height(height)

        elif block.transactions.tx_type == 'share':

            # 我同意向别人授权数据
            if block.transactions.tx_content.public_key_hash == node.address:
                pass

            # 别人同意向我授权数据
            if node.address in block.transactions.business_id:
                local_cnrp.refresh(block.transactions.tx_content.public_key_hash, CNRP_WEIGHT_DICT['local_rp_share_o2m'])

            local_cnrp.refresh_height(height)

        elif block.transactions.tx_type == 'evaluate':
            _data_rp = block.transactions.tx_content.data_evaluate
            # 我对别人数据的评价
            if block.transactions.tx_content.public_key_hash == node.address:
                _share_block = self.find_block_by_id(block.transactions.tx_content.previous_tx_id)
                local_cnrp.refresh(_share_block.transactions.tx_content.public_key_hash,
                                   round(_data_rp * 0.1 * CNRP_WEIGHT_DICT['local_rp_evaluate_m2o'], 3))
            # 他人对我的数据进行评价
            if node.address in block.transactions.business_id:
                local_cnrp.refresh(block.transactions.tx_content.public_key_hash,
                                   round(_data_rp * 0.1 * CNRP_WEIGHT_DICT['local_rp_evaluate_o2m'], 3))
            local_cnrp.refresh_height(height)
        else:
            print('Wrong type')
            return

    # 获取可直接使用的rp: global_rp + local_rp
    # public_key_hash 为tx中的属性 其来源于Node中的node_address (二者等同)
    def get_node_rp(self, address):

        # 获取 global_rp
        last_block = self.get_last_block()
        global_rp = last_block.block_header.cnrp['address']

        # 获取一级 local_rp (称为 primary_local_rp)
        local_cnrp = LocalCNRP()
        primary_local_rp = local_cnrp.get_rp(address)

        # 获取二级 local_rp (称为 secondary_local_rp)
        secondary_local_rp = 0
        if primary_local_rp == 0:

            _target_block_list = self.find_xtype_tx_relate_address(address, 'evaluate', 'pas')
            _target_block_list_len = len(_target_block_list)
            for _i in range(_target_block_list_len):

                # 变量名解释：以本地node为a; 以中间跳板节点为b; 以最终目标节点(即想要获取其rp的节点，也即参数address)为c。
                # 变量名中带有'a''b''c'的均为上述含义

                _tx = _target_block_list[_i]
                _b_address = _tx.tx_content.public_key_hash
                if _b_address in local_cnrp.get_cnrp_dict():
                    # 注意! local_rp 即上述变量 primary_local_rp 有可能是负值，计算时需注意
                    # 注意! data_score为b对c数据的打分 范围: -10 ~ 10; local_rp为本地rp 范围: -1 ~ 1
                    _b2c_data_score = _tx.tx_content.data_evaluate
                    _a2b_local_rp = local_cnrp.get_rp(_b_address)

                    secondary_local_rp += _b2c_data_score*0.1*_a2b_local_rp*0.2

            secondary_local_rp = round(secondary_local_rp / _target_block_list_len, 3)

        return global_rp, primary_local_rp, secondary_local_rp

